import {has,get,isPlainObject,merge,remove} from "lodash-es"
import { ref, mergeProps, unref } from "vue";

export type ILayoutStructItem =
  | string
  | Record<string, any>
  | ILayoutStructItem[];

export type ILayoutStruct = [string, ...ILayoutStructItem[]];
export type ILayoutItem = TOA<string>;
export type ILayout = [
  id?: ILayoutStruct | ILayoutItem,
  ...rest: ILayoutItem[]
];

//
/*  string:key 元素 如果 key 不存在，则取data 的索引作为key 值
    @id  [@id,key...] // 元素集合 如果没有 IDS ,则 ID 名称可以省略 不允许多重嵌套，ID 只能使用字符串
    // 移除多层嵌套的结构，如果需要复杂结构，请使用 ID 分组功能，
    :clz ID = @id:clz || @id || {id:"",class:"",...}   [:clz,ID1,ID2,[ID3,ID4]] // 结构信息 只会出现在第一个元素中 默认 ID default
    [{},{}]
    结构体中的 ID 都可以用 {id:ID,attr...}
    结构体中可以用 @key 的方式直接引用 元素数据，key 为元素 ID ，且支持多个元素key 以 , 号分隔
*/

const idReg = /^id:([\w-]+)?(:(.*))?$/;
const idsReg = /^ids:([\w-]+)?(:(.*))?$/;
export default function useLayout(layout: ILayout) {}

interface IUseLayout<T extends Record<string, any>> {
  data?: T[];
  defaultData?: T[];
  layout?: ILayout;
  key?: string | string[] | ((d: T) => string);
  commonAttr?: Record<string, any>;
  groupAttr?: T | ((attr: ILayoutStructItem[]) => T);
}

export class UseLayout<
  T extends Record<string, any> & { extendKeys?: string | string[] }
> {
  list: T[] = [];
  listKeys: string[] = [];
  layout: ILayout = [];
  data: T[] = [];
  defaultData: T[] = [];
  // @ts-ignore
  groupAttr: T | ((attr: Record<string, any>) => T) = {};
  commonAttr: Record<string, any> = {};
  key: string | string[] | ((d: T) => string) = ["id", "prop"];
  //
  layoutData = ref<TOA<T[]>>([]);
  layoutTempData: Record<string, T[]> = { $: [] };
  //
  constructor(params: IUseLayout<T>) {
    Object.assign(this, params);
    if (params.data || params.defaultData) {
      this.initList();
    }
  }
  // 添加 新的 data 到 data 中
  addData(data: T[] = []) {
    this.data = new Array<T>().concat(this.data, data);
    this.addList(data);
  }
  // 生成 list 和 listKey
  addList(data: T[]) {
    const [al, ak] = this.getList(data);
    this.list = [...this.list, ...al];
    this.listKeys = [...this.listKeys, ...ak];
  }
  //
  resetList() {}
  //
  initList() {
    const [list, listKeys] = this.getList([...this.defaultData, ...this.data]);
    //
    this.list = list;
    this.listKeys = listKeys;
    console.log(list, listKeys);
  }
  // 将 data 添加默认属性以及扩展属性，并追加到列表中
  getList(data: T[]): [T[], string[]] {
    const keys: string[] = data.map((d, i) => this.getDataKey(d, i));
    //
    const listKeys = [...this.listKeys, ...keys];
    const temp = [...this.list, ...data];
    //
    // console.log('........>',temp,keys,data)
    const list: T[] = data.map((d) => {
      const t: Record<string, any> = {};
      const ed = [this.commonAttr];
      const ek = new Array<string>().concat(d.extendKeys || []);
      if (ek.length > 0) {
        ek.forEach((k) => {
          const i = listKeys.indexOf(k);
          if (i > -1) {
            ed.push(temp[i]);
          }
        });
      }
      //
      return merge(t, ...ed, mergeProps(this.commonAttr, d));
    });
    //
    // console.log(list,listKeys)
    return [list, keys];
  }
  //
  getDataKey(data: T, ins: number) {
    let res = String(ins);
    const key = this.key;
    if (!key) {
      return res;
    }
    if (typeof key === "string" && has(data, key)) {
      res = get(data, key);
    }
    if (Array.isArray(key)) {
      for (let i = 0, l = key.length; i < l; i++) {
        const k = key[i];
        if (has(data, k)) {
          res = get(data, k);
        }
      }
    }
    if (typeof key === "function") {
      res = key(data);
    }

    return res || String(ins);
  }
  // 是否考虑支持多 key 映射
  getListDataByKey(key: string) {
    const ins = this.listKeys.indexOf(key);
    if (ins > -1) {
      return this.list[ins];
    }
    return undefined;
  }
  // 重置布局结果
  resetLayout() {}
  // 获取布局之后的数据集合
  getLayoutData(layout: ILayout = this.layout) {
    const [st, ...items] = layout;
    const bool =
      Array.isArray(st) && typeof st[0] === "string" && /^:/.test(st[0]);
    if (!bool) {
      return layout
        .map((d) => this.getLayoutItemData(d as ILayoutItem))
        .filter((d) => d != undefined) as T[];
    } else {
      items.map((d) => this.getLayoutItemData(d));
      const t = this.getLayoutGroupData(st);
      // 结构数据处理
      return /^::/.test(st[0]) ? t.children : t;
    }
  }
  // 获取布局元素数据
  getLayoutItemData(data: ILayoutItem) {
    // key | [key,...] | [id:ID:clz,key...] | [{id:ID,...},key....]
    if (typeof data === "string") {
      return this.getListDataByKey(data);
    }
    const isArray = Array.isArray(data);
    if (!isArray) {
      throw new Error(
        "wrong type for layout data,expect array but got" + typeof data
      );
    }
    //
    const [v0, ...keys] = data;
    const id = (/^@(\w+)?/.exec(v0) || [])[1] || "$";
    //
    if (!/^@(\w+)?$/.test(v0)) {
      this.layoutTempData.$.push(
        ...(data
          .map((d) => this.getListDataByKey(d as string))
          .filter((d) => d != undefined) as T[])
      );
    } else {
      const cls = keys
        .map((d) => this.getListDataByKey(d))
        .filter((d) => d != undefined) as T[];
      if (!has(this.layoutTempData, id)) {
        this.layoutTempData[id] = [...cls];
      } else {
        this.layoutTempData[id].push(...cls);
      }
    }
  }
  // 获取布局分组数据
  getLayoutGroupData(struct: ILayoutStructItem[]) {
    const l = struct.length;
    const def =
      typeof this.groupAttr === "function"
        ? this.groupAttr(struct)
        : this.groupAttr;
    let obj: Record<string, any> = merge({}, def);
    const children: T[] = [];
    for (let i = 0; i < l; i++) {
      const d = struct[i];
      // 判定首位是否是 class 参数
      if (i == 0 && typeof d === "string" && /^:/.test(d)) {
        const t = /^:(.*)?/.exec(d);
        if (t && t[1]) {
          obj = mergeProps({ class: t[1] }, obj);
        }
        continue;
      }
      // 是否是对像
      if (isPlainObject(d) && (d as any).id) {
        if (has(this.layoutTempData, (d as any).id)) {
          children.push(
            merge({}, def, mergeProps(def, d as any), {
              children: get(this.layoutTempData, (d as any).id),
            })
          );
        }
        continue;
      }
      // 是否是字符串 ID
      if (typeof d === "string") {
        if (/^@/.test(d)) {
          const keys = /^@(.*)/.exec(d)?.[1].split(",");
          keys &&
            children.push(
              ...(keys
                .map((d) => this.getListDataByKey(d))
                .filter((d) => d != undefined) as T[])
            );
        } else if (has(this.layoutTempData, d)) {
          children.push(
            merge({}, def, { children: get(this.layoutTempData, d) })
          );
        }
        continue;
      }
      // 是否需要递归处理
      if (Array.isArray(d)) {
        children.push(this.getLayoutGroupData(d));
        continue;
      }
    }
    obj.children = children;
    return obj as T;
  }
}

// columnItem 数据合并，提取，混合，属性扩展，属性过滤

// column 分片（过滤 + 提取），混入，过滤，。。。
export class DataUtils<T extends Record<string, any>> {
  // 为了支持链式操作
  result: T[] = [];
  //
  constructor(data: TOR<T>[]) {
    this.result = merge([], unref(data));
  }
  // 合并
  static merge<T>(
    source: T[],
    target: Record<string, any> | ((obj: T) => Record<string, any>)
  ) {}
  // 过滤
  static filter<T>(source: T[], fun: (obj: T) => boolean) {}
}
export const columnMerge = <T extends Record<string, any>>(
  column: T[],
  target: Record<string, any> | ((ojb: T) => Record<string, any>)
) => {
  // column.map(d => )
};

// 数据处理
// 数据 属性值的提取，key 复制，key 扩展 ，属性键的转换，嵌套层级压缩，嵌套层级扩展 深度遍历 广度遍历,删除属性，提取属性到另一个点
// 表达式处理 ，字符串模板:  表达式 {{}} // 变量提取 ${}
// templateParsing  expressionParsing // 表达式解析
/* 
// 默认 context : { row: 当前行，val?:当前属性值,key?:当前key ,list?:当前列表 ,ins?:当前索引，parent?: 当前父对象,data?: 当前数据集合，parents:[] 父对象列表，$:扩展属性}
rules : [
  // 具体属性转换 
  {key?:string|reg,targetKey:"{{  }}" | any[]|function,value:"{{}}" | function ,string。deep?:boolean | "DFS" | "BFS"，target:"...",once:boolean = true }
  {targetKey:function}
  //
  key 转换： [string|string[]|fun(key,context)|regexp,?string|string[]|fun(key,context)]
  value:fun(val,context) | {{ ... }},
  methods:[[fun(),exp]]
]
 */

/* rules 规则处理 */
export const ruleExp = (rules: any[], ctx: any) => {
  if (rules.length == 0) return;
  const { key, val, data, row, list, parents } = ctx;
  //
  const dels: number[] = [];
  //
  rules.map((d, i) => {
    const { key, value, once = true, deep = true, methods = [] } = d;
  });

  if (dels.length > 0) {
    remove(rules, (n, i) => dels.includes(i));
  }
};

/* 对象的遍历 */
export const objMap = (
  data: Record<any, any> | any[],
  call: (ctx: any) => any,
  parents: any[] = []
) => {
  const pd = [...parents, data];
  if (Array.isArray(data)) {
    for (let i = 0, l = data.length; i < l; i++) {
      const val = data[i];
      const ctx = { val, ins: i, list: data, data, parents: pd };
      const res = call(ctx);
      if (res === false) {
        break;
      }
      if (res === true) {
        objMap(val, call, pd);
      }
    }
  }
  if (isPlainObject(data)) {
    const keys = Object.keys(data as Record<any, any>);
    for (let i = 0, l = keys.length; i < l; i++) {
      const key = keys[i];
      const val = (data as Record<any, any>)[key];
      const ctx: any = { data, key, val, parents: pd };
      if (Array.isArray(parents.at(-1))) {
        ctx.row = data;
      }
      const res = call(ctx);
      if (res === false) {
        break;
      }
      if (res === true) {
        objMap(val, call, pd);
      }
    }
  }
};

/* 树 的遍历 */
export const DFS = () => {};
export const BFS = () => {};
